package com.nuvsoft.android.nuvgamenet.net;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import com.nuvsoft.android.nuvgamenet.GameLobby;
import com.nuvsoft.android.nuvgamenet.NuvGameNetApplication;
import com.nuvsoft.android.nuvgamenet.NuvGameNetSignal;
import com.nuvsoft.android.nuvgamenet.net.TCPHandler.TCPConnection;

public class ConnectionManager {
	NuvGameNetApplication na;

	private MulticastHandler multicast;
	private TCPHandler tcp;

	// session variables
	public TCPConnection currentHost;

	// Connection Listeners
	private volatile CopyOnWriteArrayList<ConnectionChangeListener> connectionListeners;

	// Open Lobby State variables
	Thread lobbyThread;
	TCPListener lobbyBroadcastListener;

	// Join Lobby State Variables
	public volatile List<GameLobby> openLobbies;
	MulticastListener lobbyListBroadcastListener;

	public ConnectionManager(NuvGameNetApplication na) {
		this.na = na;

		multicast = new MulticastHandler(na);
		multicast.startListening();

		openLobbies = new CopyOnWriteArrayList<GameLobby>();
		connectionListeners = new CopyOnWriteArrayList<ConnectionChangeListener>();

		tcp = new TCPHandler();
		tcp.startListening();
	}

	public boolean joinLobby(String address) {
		TCPConnection newCon = tcp.connect(address);
		if (newCon != null) {
			currentHost = newCon;
		} else {
			return false;
		}

		for (ConnectionChangeListener c : connectionListeners) {
			c.joinLobby();
		}

		lobbyBroadcastListener = new TCPListener() {
			@Override
			public void onReceiveUnverfied(TCPConnection t, String tag,
					String[] data) {
				// no unverified messages allowed
			}

			@Override
			public void onReceiveVerified(TCPConnection t, String tag,
					String[] data) {
				if (currentHost == t) {
					if (tag
							.equalsIgnoreCase(NuvGameNetSignal.START_GAME
									.name())) {
						for (ConnectionChangeListener c : connectionListeners) {
							c.startGame();
						}
					} else if (tag.equalsIgnoreCase(NuvGameNetSignal.LOBBY_LIST
							.name())) {
						tcp.connect(data[0]);
					}
				}
			}
		};
		tcp.addTCPListener(lobbyBroadcastListener);
		return true;
	}

	/**
	 * 
	 */
	public void startNewLobby() {
		tcp.setHost(true);
		tcp.setSessionOpen(true);
		lobbyThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (tcp.isSessionOpen()) {
					try {
						// broadcast lobby
						multicast.sendData(String.format("%s,%s,%s",
								NuvGameNetSignal.OPEN_LOBBY.name(),
								NuvGameNetApplication.getNick(), na
										.getLocalIpAddress()));

						// broadcast players
						for (TCPConnection t0 : tcp.getConnections()) {
							for (TCPConnection t1 : tcp.getConnections()) {
								if (t0 != t1)
									t0.send(NuvGameNetSignal.LOBBY_LIST.name(),
											new String[] { t1.getAddress() });
							}
							Thread.sleep(500);
						}
						Thread.sleep(1000);
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		});
		lobbyThread.start();
	}

	public void closeLobby() {
		if (lobbyThread != null) {
			if (lobbyThread.isAlive()) {
				lobbyThread.interrupt();
			}
			multicast.sendData(String.format("%s,%s,%s",
					NuvGameNetSignal.CLOSE_LOBBY.name(), NuvGameNetApplication
							.getNick(), na.getLocalIpAddress()));
		}
	}

	public void stopLobbyListener() {
		multicast.removeBroadcastListener(lobbyListBroadcastListener);
		lobbyListBroadcastListener = null;
		openLobbies.clear();
	}

	public void startLobbyListener() {
		lobbyListBroadcastListener = new MulticastListener() {
			@Override
			public void onReceiveBroadcast(String message) {
				if (message.startsWith(NuvGameNetSignal.OPEN_LOBBY.name())) {
					String[] split = message.split(",");
					String hostNick = split[1];
					String hostAddr = split[2];
					for (GameLobby gl : openLobbies) {
						if (gl.getAddress().equalsIgnoreCase(hostAddr)
								&& gl.getNick().equalsIgnoreCase(hostNick)) {
							return;
						}
					}
					GameLobby lobby = new GameLobby(hostNick, hostAddr, na);
					openLobbies.add(lobby);
					synchronized (connectionListeners) {
						for (ConnectionChangeListener cl : connectionListeners) {
							cl.lobbyAdded();
						}
					}
				} else if (message.startsWith(NuvGameNetSignal.CLOSE_LOBBY
						.name())) {
					String[] split = message.split(",");
					String hostNick = split[1];
					String hostAddr = split[2];
					GameLobby toClose = null;
					for (GameLobby gl : openLobbies) {
						if (gl.getAddress().equalsIgnoreCase(hostAddr)
								&& gl.getNick().equalsIgnoreCase(hostNick)) {
							toClose = gl;
							break;
						}
					}
					if (toClose != null) {
						openLobbies.remove(toClose);
						synchronized (connectionListeners) {
							for (ConnectionChangeListener cl : connectionListeners) {
								cl.lobbyRemoved();
							}
						}
					}
				}
			}
		};
		multicast.addBroadcastListener(lobbyListBroadcastListener);
	}

	public void addConnectionListener(ConnectionChangeListener cl) {
		synchronized (connectionListeners) {
			connectionListeners.add(cl);
		}
	}

	public void removeConnectionListener(ConnectionChangeListener cl) {
		synchronized (connectionListeners) {
			connectionListeners.remove(cl);
		}
	}

	public void startGame() {
		if (tcp.isHost()) {
			tcp.setSessionOpen(false);
			tcp.sendToAll(NuvGameNetSignal.START_GAME.name(), null);
		} else {
			stopLobbyListener();
		}
	}

	public TCPHandler getTCPHandler() {
		return tcp;
	}
}
